package com.cqsd.ioc.core.context;

import com.cqsd.ioc.annotation.*;
import com.cqsd.ioc.exception.CreateApplicationException;
import com.cqsd.ioc.handler.AnnotationHandler;
import com.cqsd.ioc.trait.Application;
import com.cqsd.ioc.trait.Aware;
import com.cqsd.ioc.trait.BeanInfo;
import com.cqsd.ioc.trait.use.*;
import com.cqsd.ioc.util.AnnotationUtil;
import com.cqsd.string.StringUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

/**
 * @author caseycheng
 * @date 2023/3/11-12:22
 **/
public class ApplicationContext extends BaseContext implements Application {
    private final Class<?> mainApplicationClass;
    /**
     * 日志
     */
    private static final Logger log = LogManager.getLogger(ApplicationContext.class);
    /**
     * classLoader
     */
    private static final ClassLoader APP_CLASS_LOADER = ApplicationContext.class.getClassLoader();
    /**
     * 需要自运行的Bean方法
     */
    private final Set<BeanRun> runQueue = new HashSet<>();
    /**
     * 线程池
     */
    private static ExecutorService EXECUTOR_SERVICE = null;
    /**
     * java配置 注解处理器
     */
    private static final AnnotationHandler CONFIG_ANNOTATION_HANDLER = new AnnotationHandler();
    /**
     * 用户注解处理器
     */
    private static final AnnotationHandler USER_ANNOTATION_HANDLER = new AnnotationHandler();

    /**
     * 用户定义的方法注解处理器
     */
    private static final AnnotationHandler METHOD_ANNOTATION_HANDLER = new AnnotationHandler();

    @NotNull
    @Contract("_ -> new")
    public static Application run(@NotNull Class<?> mainApplicationClass) {
        return new ApplicationContext(mainApplicationClass);
    }

    public ApplicationContext(Class<?> mainApplicationClass) {
        this.mainApplicationClass = mainApplicationClass;
        final ApplicationBoot boot = AnnotationUtil.findAnnotation(mainApplicationClass, ApplicationBoot.class);
        if (boot != null) {
            log.info("---------------------------------------微型Ioc正在启动---------------------------------------\r\n");
            final var applicationConfig = AnnotationUtil.findAnnotation(mainApplicationClass, ApplicationConfig.class);
            super.initEnvConfig(applicationConfig);
            final List<Annotation> annotations = List.of(applicationConfig.value().getAnnotations());
            this.initAnnoHandler();
            for (Annotation annotation : annotations) {
                if (CONFIG_ANNOTATION_HANDLER.isHandler(annotation.annotationType())) {
                    log.trace("正在处理{}注解", annotation.annotationType().getSimpleName());
                    CONFIG_ANNOTATION_HANDLER.handler(annotation, null);
                    log.trace("{}注解处理完毕", annotation.annotationType().getSimpleName());
                }
            }
            super.initShutdownHook();
            if (EXECUTOR_SERVICE == null) {
                log.info("没有设置线程池，启用默认线程池WorkStealingPool");
                EXECUTOR_SERVICE = Executors.newWorkStealingPool();
            }
            log.info("---------------------------------------微型Ioc启动完成---------------------------------------\r\n");
            if (runQueue.size() > 0) {
                log.info("-------------------------------------正在向线程池提交bean任务-------------------------------------\r\n");
                final List<? extends Future<?>> futureList = runQueue.stream()
                        .map(run -> EXECUTOR_SERVICE.submit(run::run))
                        .toList();
                log.trace("主线程正在等待执行{}个任务", futureList.size());
                for (int i = 0, futureListSize = futureList.size(); i < futureListSize; i++) {
                    Future<?> future = futureList.get(i);
                    while (!future.isDone()) {
                        try {
                            TimeUnit.NANOSECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    log.trace("第{}个任务结束", i + 1);
                }
                log.trace("任务结束");
            }
        } else {
            throw new CreateApplicationException("该class不是一个应用程序引导类");
        }
    }


    /**
     * 初始化解析JavaConfig里的注解处理器
     * ComponentScan必须在这里初始化，因为需要捕获一些外部变量
     */
    private void initAnnoHandler() {
        CONFIG_ANNOTATION_HANDLER.addHandler(ComponentScan.class, (a, o) -> scanComponent(a.value()));
        CONFIG_ANNOTATION_HANDLER.addHandler(ComponentScans.class, (a, o) -> {
            final ComponentScan[] scans = a.value();
            for (ComponentScan item : scans) {
                CONFIG_ANNOTATION_HANDLER.handler(item, o);
            }
        });
    }

    /**
     * 对class对象进行扫描 扫描完毕后做一些接口上的处理
     *
     * @param path
     */
    private void scanComponent(String path) {
        //包名为被[.]切分的第一个数组 例如我的包名为com.cqsd {[com],[cqsd]} com为包名
        String packageName = path.split("\\.")[0];
        //将.路径替换为文件系统可访问的路径
        path = path.replace('.', '/');
        //通过被替换的文件系统路径获取资源url
        final URL resource = APP_CLASS_LOADER.getResource(path);
        //通过url创建file对象
        File file = new File(Objects.requireNonNull(resource).getFile());
        //这里递归扫描文件夹查找class对象
        this.searchClassFile(packageName, file);
        //扫描bean对象
        this.beanHeader();
    }

    /**
     * 提交一个bean池，对bean池进行处理
     */
    private void beanHeader() {
        registerBeans(super.getBeanInfoManager().getBeanInfos());
    }

    /**
     * 将自己解析的bean交给容器管理
     *
     * @param beanInfo
     */
    @Override
    public void registerBean(BeanInfo beanInfo) throws NullPointerException {
        final Class<?> type = beanInfo.getType();
        final var name = beanInfo.getName();
        if (StringUtil.hasLength(name)) {
            final var msg = String.format("类型为%s未设置标识", type.getSimpleName());
            log.error(msg);
            throw new NullPointerException(msg);
        }
        beanBeforeProcess(beanInfo);
        beanProcess(beanInfo);
    }

    /**
     * 将解析的bean集合交给容器管理
     *
     * @param beanInfoCollection
     */
    @Override
    public void registerBeans(Collection<BeanInfo> beanInfoCollection) {
        Objects.requireNonNull(beanInfoCollection);
        for (BeanInfo info : beanInfoCollection) {
            beanBeforeProcess(info);
        }
        for (BeanInfo info : beanInfoCollection) {
            beanProcess(info);
        }
    }

    /**
     * 正式处理
     *
     * @param info
     */
    private void beanProcess(BeanInfo info) {
        final var beanName = info.getName();
        final Class<?> type = info.getType();
        final Annotation[] annotations = type.getAnnotations();
        final var singleObjects = super.getBeanInfoManager().getSingleObjects();
        //对象在单例池中不存在才会被调用父类方法来创建对象
        if (!singleObjects.containsBeanName(beanName)) {
            if (info.isSingle()) {
                singleObjects.set(beanName, super.createBean(info));
            }
        }
        //接口处理
        if (Aware.class.isAssignableFrom(type)) {
            if (ApplicationAware.class.isAssignableFrom(type)) {
                final ApplicationAware bean = (ApplicationAware) getBean(type);
                bean.setApplication(this);
            }
        }
        //找出需要运行的方法
        if (BeanRun.class.isAssignableFrom(type)) {
            final BeanRun bean = (BeanRun) getBean(type);
            runQueue.add(bean);
        }
        //找出程序关闭时的对象回收方法。
        if (DestructFunction.class.isAssignableFrom(type)) {
            final DestructFunction bean = (DestructFunction) getBean(type);
            super.addDestruct(bean);
        }
        //对注解进行处理
        for (Annotation annotation : annotations) {
            if (USER_ANNOTATION_HANDLER.isHandler(annotation.annotationType())) {
                log.info("正在为{}处理用户注解{}", info.getName(), annotation.annotationType().getSimpleName());
                USER_ANNOTATION_HANDLER.handler(annotation, getBean(type));
                log.info("{}的用户注解{}处理完成", info.getName(), annotation.annotationType().getSimpleName());
            }
        }
    }

    /**
     * 预处理
     *
     * @param info
     */
    private void beanBeforeProcess(BeanInfo info) {
        final var manager = super.getBeanInfoManager();
        final var beanInfoMap = manager.beanNameInfoMap();
        final var classBeanInfoMap = manager.beanClassInfoMap();
        final Class<?> type = info.getType();
        if (BeanPostProcess.class.isAssignableFrom(type)) {
            super.beanPostProcessList.add((BeanPostProcess) super.getBean(type));
        }
        if (FactoryBean.class.isAssignableFrom(type)) {
            final FactoryBean factory = (FactoryBean) super.getBean(type);
            final Class<?> refType = factory.getType();
            final var optionalBeanInfo = beanInfoMap
                    .values()
                    .stream()
                    .filter(t -> t.getType().equals(refType))
                    .findFirst();
            optionalBeanInfo.ifPresent(beanInfo -> {
                beanInfo.setFactory(factory);
            });
        }
        if (Arrays.stream(info.getAllMethods()).anyMatch(method -> method.isAnnotationPresent(Bean.class))) {
            Arrays.stream(info.getAllMethods())
                    .filter(method -> method.isAnnotationPresent(Bean.class))
                    .forEach(method -> {
                        method.setAccessible(true);
                        final Class<?> returnType = method.getReturnType();
                        final var beanInfo = classBeanInfoMap.get(returnType);
                        beanInfo.setFactory(new FactoryBean() {
                            @Override
                            public Class<?> getType() {
                                return returnType;
                            }

                            @Override
                            public Object create() {
                                final var parameter = method.getParameterTypes();
                                final var args = Arrays.stream(parameter)
                                        .map(ApplicationContext.super::getBean).toArray();
                                try {
                                    return method.invoke(getBean(type), args);
                                } catch (IllegalAccessException | InvocationTargetException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        });
                    });
        }
    }

    /**
     * 递归扫描class
     *
     * @param packageName
     * @param file
     */
    @Contract("null,null->fail;null,_->fail;_,null->fail")
    private void searchClassFile(String packageName, File file) {
        Objects.requireNonNull(file);
        String fileName = file.getAbsolutePath();
        //将文件夹名字转换为Java包名
        fileName = fileName.substring(fileName.indexOf(packageName)).replace('/', '.');
        //如果当前是一个文件并且是一个class文件就开始扫描
        if (file.isFile() && fileName.endsWith(".class")) {
            try {
                //去除.class 获得类的全命名
                final String className = fileName.substring(0, fileName.indexOf(".class"));
                //通过类加载起加载定位到的class文件
                Class<?> clazz = APP_CLASS_LOADER.loadClass(className);
                super.loadClass(clazz);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            //如果是目录，就获取目录里的所有文件，并递归调用。
            for (File f : Objects.requireNonNull(file.listFiles())) {
                searchClassFile(packageName, f);
            }
        }
    }


    /**
     * 向父类提供单例池
     *
     * @return
     */
    @Override
    public BeanManager.SingleObjects getSingleObjects() {
        return super.getBeanInfoManager().getSingleObjects();
    }

    /**
     * 获取容器的线程池，现在只是暂用，之后会进行优化 TODO
     * 少用三元，要打日志
     *
     * @return
     */
    public static ExecutorService getExecutorService() {
        if (EXECUTOR_SERVICE == null) {
            log.info("没有设置线程池，启用默认线程池WorkStealingPool");
            return EXECUTOR_SERVICE = Executors.newWorkStealingPool();
        } else {
            return EXECUTOR_SERVICE;
        }
    }

    /**
     * 设置容器使用的线程池
     *
     * @param executorService 线程池
     */
    public static void setExecutorService(ExecutorService executorService) {
        EXECUTOR_SERVICE = executorService;
    }

    /**
     * 返回线程池有没有设置 false为未设置
     *
     * @return
     */
    public static boolean isExecutorService() {
        return EXECUTOR_SERVICE != null;
    }

    /**
     * 用户定义的注解一般都在这里添加
     */
    public static <A extends Annotation> void addAnnotationHandlers(Class<? extends A> type, BiConsumer<? super A, Object> action) {
        log.debug("已添加{}的注解处理器", type);
        USER_ANNOTATION_HANDLER.addHandler(type, action);
    }

    /**
     * 配置类的注解在这里添加
     *
     * @param type
     * @param action
     * @param <A>
     */
    public static <A extends Annotation> void addConfigAnnotationHandler(Class<? extends A> type, BiConsumer<? super A, Object> action) {
        log.debug("已添加{}的注解处理器", type);
        CONFIG_ANNOTATION_HANDLER.addHandler(type, action);
    }

    /**
     * 用户定义的方法注解一般都在这里添加
     * 用于处理方法上面的注解，起初我以为class上获取到的所有注解包含了这个类的所有注解，可惜不是，我裂开。
     */
    public static <A extends Annotation> void addMethodAnnotationHandlers(Class<? extends A> type, BiConsumer<? super A, Object> action) {
        log.debug("已添加{}的注解处理器", type);
        METHOD_ANNOTATION_HANDLER.addHandler(type, action);
    }
}
